/*
 * Copyright (c) 2003 NONAKA Kimihiro
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "compiler.h"
#include "cpu.h"
#include "ia32.mcr"

#include "string_inst.h"


/* movs */
void
MOVSB_XbYb(void)
{
	UINT8 tmp;

	CPU_WORKCLOCK(5);
	CPU_INST_SEGREG_INDEX = DS_FIX;
	if (!CPU_INST_AS32) {
		tmp = cpu_vmemoryread(CPU_INST_SEGREG_INDEX, CPU_SI);
		cpu_vmemorywrite(CPU_ES_INDEX, CPU_DI, tmp);
		CPU_SI += STRING_DIR;
		CPU_DI += STRING_DIR;
	} else {
		tmp = cpu_vmemoryread(CPU_INST_SEGREG_INDEX, CPU_ESI);
		cpu_vmemorywrite(CPU_ES_INDEX, CPU_EDI, tmp);
		CPU_ESI += STRING_DIR;
		CPU_EDI += STRING_DIR;
	}
}

void
MOVSW_XwYw(void)
{
	UINT16 tmp;

	CPU_WORKCLOCK(5);
	CPU_INST_SEGREG_INDEX = DS_FIX;
	if (!CPU_INST_AS32) {
		tmp = cpu_vmemoryread_w(CPU_INST_SEGREG_INDEX, CPU_SI);
		cpu_vmemorywrite_w(CPU_ES_INDEX, CPU_DI, tmp);
		CPU_SI += STRING_DIRx2;
		CPU_DI += STRING_DIRx2;
	} else {
		tmp = cpu_vmemoryread_w(CPU_INST_SEGREG_INDEX, CPU_ESI);
		cpu_vmemorywrite_w(CPU_ES_INDEX, CPU_EDI, tmp);
		CPU_ESI += STRING_DIRx2;
		CPU_EDI += STRING_DIRx2;
	}
}

void
MOVSD_XdYd(void)
{
	UINT32 tmp;

	CPU_WORKCLOCK(5);
	CPU_INST_SEGREG_INDEX = DS_FIX;
	if (!CPU_INST_AS32) {
		tmp = cpu_vmemoryread_d(CPU_INST_SEGREG_INDEX, CPU_SI);
		cpu_vmemorywrite_d(CPU_ES_INDEX, CPU_DI, tmp);
		CPU_SI += STRING_DIRx4;
		CPU_DI += STRING_DIRx4;
	} else {
		tmp = cpu_vmemoryread_d(CPU_INST_SEGREG_INDEX, CPU_ESI);
		cpu_vmemorywrite_d(CPU_ES_INDEX, CPU_EDI, tmp);
		CPU_ESI += STRING_DIRx4;
		CPU_EDI += STRING_DIRx4;
	}
}


/* cmps */
void
CMPSB_XbYb(void)
{
	UINT32 src, dst, res;

	CPU_WORKCLOCK(8);
	CPU_INST_SEGREG_INDEX = DS_FIX;
	if (!CPU_INST_AS32) {
		dst = cpu_vmemoryread(CPU_INST_SEGREG_INDEX, CPU_SI);
		src = cpu_vmemoryread(CPU_ES_INDEX, CPU_DI);
		BYTE_SUB(res, dst, src);
		CPU_SI += STRING_DIR;
		CPU_DI += STRING_DIR;
	} else {
		dst = cpu_vmemoryread(CPU_INST_SEGREG_INDEX, CPU_ESI);
		src = cpu_vmemoryread(CPU_ES_INDEX, CPU_EDI);
		BYTE_SUB(res, dst, src);
		CPU_ESI += STRING_DIR;
		CPU_EDI += STRING_DIR;
	}
}

void
CMPSW_XwYw(void)
{
	UINT32 src, dst, res;

	CPU_WORKCLOCK(8);
	CPU_INST_SEGREG_INDEX = DS_FIX;
	if (!CPU_INST_AS32) {
		dst = cpu_vmemoryread_w(CPU_INST_SEGREG_INDEX, CPU_SI);
		src = cpu_vmemoryread_w(CPU_ES_INDEX, CPU_DI);
		WORD_SUB(res, dst, src);
		CPU_SI += STRING_DIRx2;
		CPU_DI += STRING_DIRx2;
	} else {
		dst = cpu_vmemoryread_w(CPU_INST_SEGREG_INDEX, CPU_ESI);
		src = cpu_vmemoryread_w(CPU_ES_INDEX, CPU_EDI);
		WORD_SUB(res, dst, src);
		CPU_ESI += STRING_DIRx2;
		CPU_EDI += STRING_DIRx2;
	}
}

void
CMPSD_XdYd(void)
{
	UINT32 src, dst, res;

	CPU_WORKCLOCK(8);
	CPU_INST_SEGREG_INDEX = DS_FIX;
	if (!CPU_INST_AS32) {
		dst = cpu_vmemoryread_d(CPU_INST_SEGREG_INDEX, CPU_SI);
		src = cpu_vmemoryread_d(CPU_ES_INDEX, CPU_DI);
		DWORD_SUB(res, dst, src);
		CPU_SI += STRING_DIRx4;
		CPU_DI += STRING_DIRx4;
	} else {
		dst = cpu_vmemoryread_d(CPU_INST_SEGREG_INDEX, CPU_ESI);
		src = cpu_vmemoryread_d(CPU_ES_INDEX, CPU_EDI);
		DWORD_SUB(res, dst, src);
		CPU_ESI += STRING_DIRx4;
		CPU_EDI += STRING_DIRx4;
	}
}


/* scas */
void
SCASB_ALXb(void)
{
	UINT32 src, dst, res;

	CPU_WORKCLOCK(7);
	dst = CPU_AL;
	if (!CPU_INST_AS32) {
		src = cpu_vmemoryread(CPU_ES_INDEX, CPU_DI);
		BYTE_SUB(res, dst, src);
		CPU_DI += STRING_DIR;
	} else {
		src = cpu_vmemoryread(CPU_ES_INDEX, CPU_EDI);
		BYTE_SUB(res, dst, src);
		CPU_EDI += STRING_DIR;
	}
}

void
SCASW_AXXw(void)
{
	UINT32 src, dst, res;

	CPU_WORKCLOCK(7);
	dst = CPU_AX;
	if (!CPU_INST_AS32) {
		src = cpu_vmemoryread_w(CPU_ES_INDEX, CPU_DI);
		WORD_SUB(res, dst, src);
		CPU_DI += STRING_DIRx2;
	} else {
		src = cpu_vmemoryread_w(CPU_ES_INDEX, CPU_EDI);
		WORD_SUB(res, dst, src);
		CPU_EDI += STRING_DIRx2;
	}
}

void
SCASD_EAXXd(void)
{
	UINT32 src, dst, res;

	CPU_WORKCLOCK(7);
	dst = CPU_EAX;
	if (!CPU_INST_AS32) {
		src = cpu_vmemoryread_d(CPU_ES_INDEX, CPU_DI);
		DWORD_SUB(res, dst, src);
		CPU_DI += STRING_DIRx4;
	} else {
		src = cpu_vmemoryread_d(CPU_ES_INDEX, CPU_EDI);
		DWORD_SUB(res, dst, src);
		CPU_EDI += STRING_DIRx4;
	}
}


/* lods */
void
LODSB_ALXb(void)
{

	CPU_WORKCLOCK(5);
	CPU_INST_SEGREG_INDEX = DS_FIX;
	if (!CPU_INST_AS32) {
		CPU_AL = cpu_vmemoryread(CPU_INST_SEGREG_INDEX, CPU_SI);
		CPU_SI += STRING_DIR;
	} else {
		CPU_AL = cpu_vmemoryread(CPU_INST_SEGREG_INDEX, CPU_ESI);
		CPU_ESI += STRING_DIR;
	}
}

void
LODSW_AXXw(void)
{

	CPU_WORKCLOCK(5);
	CPU_INST_SEGREG_INDEX = DS_FIX;
	if (!CPU_INST_AS32) {
		CPU_AX = cpu_vmemoryread_w(CPU_INST_SEGREG_INDEX, CPU_SI);
		CPU_SI += STRING_DIRx2;
	} else {
		CPU_AX = cpu_vmemoryread_w(CPU_INST_SEGREG_INDEX, CPU_ESI);
		CPU_ESI += STRING_DIRx2;
	}
}

void
LODSD_EAXXd(void)
{

	CPU_WORKCLOCK(5);
	CPU_INST_SEGREG_INDEX = DS_FIX;
	if (!CPU_INST_AS32) {
		CPU_EAX = cpu_vmemoryread_d(CPU_INST_SEGREG_INDEX, CPU_SI);
		CPU_SI += STRING_DIRx4;
	} else {
		CPU_EAX = cpu_vmemoryread_d(CPU_INST_SEGREG_INDEX, CPU_ESI);
		CPU_ESI += STRING_DIRx4;
	}
}


/* stos */
void
STOSB_YbAL(void)
{

	CPU_WORKCLOCK(3);
	if (!CPU_INST_AS32) {
		cpu_vmemorywrite(CPU_ES_INDEX, CPU_DI, CPU_AL);
		CPU_DI += STRING_DIR;
	} else {
		cpu_vmemorywrite(CPU_ES_INDEX, CPU_EDI, CPU_AL);
		CPU_EDI += STRING_DIR;
	}
}

void
STOSW_YwAX(void)
{

	CPU_WORKCLOCK(3);
	if (!CPU_INST_AS32) {
		cpu_vmemorywrite_w(CPU_ES_INDEX, CPU_DI, CPU_AX);
		CPU_DI += STRING_DIRx2;
	} else {
		cpu_vmemorywrite_w(CPU_ES_INDEX, CPU_EDI, CPU_AX);
		CPU_EDI += STRING_DIRx2;
	}
}

void
STOSD_YdEAX(void)
{

	CPU_WORKCLOCK(3);
	if (!CPU_INST_AS32) {
		cpu_vmemorywrite_d(CPU_ES_INDEX, CPU_DI, CPU_EAX);
		CPU_DI += STRING_DIRx4;
	} else {
		cpu_vmemorywrite_d(CPU_ES_INDEX, CPU_EDI, CPU_EAX);
		CPU_EDI += STRING_DIRx4;
	}
}


/* repeat */
void
_REPNE(void)
{

	CPU_INST_REPUSE = 0xf2;
}

void
_REPE(void)
{

	CPU_INST_REPUSE = 0xf3;
}


/* ins */
void
INSB_YbDX(void)
{
	UINT8 data;

	CPU_WORKCLOCK(12);
	data = cpu_in(CPU_DX);
	if (!CPU_INST_AS32) {
		cpu_vmemorywrite(CPU_ES_INDEX, CPU_DI, data);
		CPU_DI += STRING_DIR;
	} else {
		cpu_vmemorywrite(CPU_ES_INDEX, CPU_EDI, data);
		CPU_EDI += STRING_DIR;
	}
}

void
INSW_YwDX(void)
{
	UINT16 data;

	CPU_WORKCLOCK(12);
	data = cpu_in_w(CPU_DX);
	if (!CPU_INST_AS32) {
		cpu_vmemorywrite_w(CPU_ES_INDEX, CPU_DI, data);
		CPU_DI += STRING_DIRx2;
	} else {
		cpu_vmemorywrite_w(CPU_ES_INDEX, CPU_EDI, data);
		CPU_EDI += STRING_DIRx2;
	}
}

void
INSD_YdDX(void)
{
	UINT32 data;

	CPU_WORKCLOCK(12);
	data = cpu_in_d(CPU_DX);
	if (!CPU_INST_AS32) {
		cpu_vmemorywrite_d(CPU_ES_INDEX, CPU_DI, data);
		CPU_DI += STRING_DIRx4;
	} else {
		cpu_vmemorywrite_d(CPU_ES_INDEX, CPU_EDI, data);
		CPU_EDI += STRING_DIRx4;
	}
}


/* outs */
void
OUTSB_DXXb(void)
{
	UINT8 data;

	CPU_WORKCLOCK(14);
	CPU_INST_SEGREG_INDEX = DS_FIX;
	if (!CPU_INST_AS32) {
		data = cpu_vmemoryread(CPU_INST_SEGREG_INDEX, CPU_SI);
		cpu_out(CPU_DX, data);
		CPU_SI += STRING_DIR;
	} else {
		data = cpu_vmemoryread(CPU_INST_SEGREG_INDEX, CPU_ESI);
		cpu_out(CPU_DX, data);
		CPU_ESI += STRING_DIR;
	}
}

void
OUTSW_DXXw(void)
{
	UINT16 data;

	CPU_WORKCLOCK(14);
	CPU_INST_SEGREG_INDEX = DS_FIX;
	if (!CPU_INST_AS32) {
		data = cpu_vmemoryread_w(CPU_INST_SEGREG_INDEX, CPU_SI);
		cpu_out_w(CPU_DX, data);
		CPU_SI += STRING_DIRx2;
	} else {
		data = cpu_vmemoryread_w(CPU_INST_SEGREG_INDEX, CPU_ESI);
		cpu_out_w(CPU_DX, data);
		CPU_ESI += STRING_DIRx2;
	}
}

void
OUTSD_DXXd(void)
{
	UINT32 data;

	CPU_WORKCLOCK(14);
	CPU_INST_SEGREG_INDEX = DS_FIX;
	if (!CPU_INST_AS32) {
		data = cpu_vmemoryread_d(CPU_INST_SEGREG_INDEX, CPU_SI);
		cpu_out_d(CPU_DX, data);
		CPU_SI += STRING_DIRx4;
	} else {
		data = cpu_vmemoryread_d(CPU_INST_SEGREG_INDEX, CPU_ESI);
		cpu_out_d(CPU_DX, data);
		CPU_ESI += STRING_DIRx4;
	}
}
